package com.kotlinvben.service

import com.kotlinvben.entity.Menu
import com.kotlinvben.exception.MenuCircularReferenceException
import com.kotlinvben.exception.MenuNameExistsException
import com.kotlinvben.exception.MenuNotFoundException
import com.kotlinvben.exception.MenuParentNotFoundException
import com.kotlinvben.repository.MenuRepository
import com.kotlinvben.repository.UserRepository
import com.kotlinvben.utils.SecurityUtils
import com.kotlinvben.vo.MenuCreateVO
import com.kotlinvben.vo.MenuUpdateVO
import org.springframework.data.domain.Sort
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional

@Service
@Transactional
class MenuService(
    private val menuRepository: MenuRepository,
    private val userRepository: UserRepository
) {
    fun getMenuById(id: Long): Menu? {
        return menuRepository.findById(id).orElse(null)
    }

    fun getMenus(): List<Menu> {
        // 1. 查询所有根菜单（pid为null）
        val rootMenus = menuRepository.findByPidIsNull()
            .sortedBy { it.sort }

        // 2. 递归查询每个根菜单的子菜单
        rootMenus.forEach { root ->
            findChildren(root)
        }

        return rootMenus
    }

    private fun findChildren(menu: Menu) {
        // 1. 查询当前菜单的子菜单
        val children = menuRepository.findByPid(menu.id!!)
            .sortedBy { it.sort }

        if (children.isNotEmpty()) {
            // 2. 将子菜单添加到当前菜单
            menu.children = children.toMutableList()

            // 3. 递归查询每个子菜单的子菜单
            children.forEach { child ->
                findChildren(child)
            }
        }
    }

    fun createMenu(menuCreateVO: MenuCreateVO): Menu {
        // 检查菜单名称是否已存在
        if (menuRepository.existsByName(menuCreateVO.name)) {
            throw MenuNameExistsException(menuCreateVO.name)
        }

        // 检查父菜单是否存在
        menuCreateVO.pid?.let { parentId ->
            if (!menuRepository.existsById(parentId)) {
                throw MenuParentNotFoundException(parentId)
            }
        }

        val username = SecurityUtils.getCurrentUsername()
        val menu = Menu(
            name = menuCreateVO.name,
            authCode = menuCreateVO.authCode,
            path = menuCreateVO.path,
            type = menuCreateVO.type,
            component = menuCreateVO.component,
            sort = menuCreateVO.sort,
            pid = menuCreateVO.pid,
            status = menuCreateVO.status,
            remark = menuCreateVO.remark,
            meta = menuCreateVO.meta,
            createBy = username,
            modifyBy = username
        )

        return menuRepository.save(menu)
    }

    fun updateMenu(id: Long, menuUpdateVO: MenuUpdateVO): Menu {
        val menu = getMenuById(id) ?: throw MenuNotFoundException(id)

        // 检查菜单名称是否已存在（排除自身）
        if (menuRepository.existsByName(menuUpdateVO.name) && menu.name != menuUpdateVO.name) {
            throw MenuNameExistsException(menuUpdateVO.name)
        }

        // 检查父菜单是否存在
        menuUpdateVO.pid?.let { parentId ->
            if (!menuRepository.existsById(parentId)) {
                throw MenuParentNotFoundException(parentId)
            }
            // 检查是否形成循环引用
            if (parentId == id) {
                throw MenuCircularReferenceException(id, parentId)
            }
        }

        menu.name = menuUpdateVO.name
        menu.authCode = menuUpdateVO.authCode
        menu.path = menuUpdateVO.path
        menu.component = menuUpdateVO.component
        menu.sort = menuUpdateVO.sort
        menu.pid = menuUpdateVO.pid
        menu.status = menuUpdateVO.status
        menu.remark = menuUpdateVO.remark
        menu.meta = menuUpdateVO.meta
        menu.modifyBy = SecurityUtils.getCurrentUsername()

        return menuRepository.save(menu)
    }

    fun deleteMenu(id: Long) {
        if (!menuRepository.existsById(id)) {
            throw MenuNotFoundException(id)
        }
        menuRepository.deleteById(id)
    }

    fun pathExists(id: Long, path: String): Boolean? {
        return menuRepository.existsByPathAndIdNot(path, id)
    }

    fun nameExists(id: Long, name: String): Boolean? {
        return menuRepository.existsByNameAndIdNot(name, id)
    }

    fun getAllMenus(): List<Menu>? {
        var menuList = emptyList<Menu>()
        SecurityUtils.getCurrentUserId()?.let { userId ->
            userRepository.findById(userId).ifPresent { user ->
                if (user.roles.any { it.code == "super" }) {
                    menuList = buildMenuTree(menuRepository.findAll(Sort.by(Sort.Direction.ASC, "sort")))
                } else {
                    val mList = user.roles.flatMap { role -> role.menus }.toList()
                    menuList = buildMenuTree(mList)
                }
            }
        }
        return menuList
    }

    /**
     * 递归构建菜单树
     * @param menuList 所有菜单列表
     * @return 构建好的菜单树
     */
    private fun buildMenuTree(menuList: List<Menu>): List<Menu> {
        // 找出所有顶级菜单（pid 为 null 的菜单）
        val rootMenus = menuList.filter { it.pid == null }
        
        // 递归设置子菜单
        return rootMenus.map { menu ->
            menu.children = findChildren(menu, menuList).toMutableList()
            menu
        }
    }

    /**
     * 递归查找子菜单
     * @param parentMenu 父菜单
     * @param menuList 所有菜单列表
     * @return 子菜单列表
     */
    private fun findChildren(parentMenu: Menu, menuList: List<Menu>): List<Menu> {
        // 找出直接子菜单
        val children = menuList.filter { it.pid == parentMenu.id && it.type == "menu" }
        
        // 递归设置每个子菜单的子菜单
        return children.map { child ->
            child.children = findChildren(child, menuList).toMutableList()
            child
        }
    }

}